#include "nnslitedatapage.h"

NNSLITE_RET nnsliteDataPageInit(nnsliteDataPage* page)
{
	page->firstCell = (nnsliteMemCell*)malloc(sizeof(nnsliteMemCell));
	page->firstCell->nextCell = NULL;
	page->firstCell->usedsize = 0;
	page->firstCell->capacity = NNSLITE_CELL_CAPACITY;
	page->lastCell = page->firstCell;
	page->totalcapacity = NNSLITE_CELL_CAPACITY;
	page->totalusedsize = 0;
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteDataPageFree(nnsliteDataPage* page)
{
	nnsliteMemCell* cell = page->firstCell;
	nnsliteMemCell* nextCell;
	while (cell != NULL)
	{
		nextCell = cell->nextCell;
		free(cell);
		cell = nextCell;
	}
	page->firstCell = NULL;
	page->lastCell = NULL;
	page->totalcapacity = 0;
	page->totalusedsize = 0;
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteDataPageWrite(nnsliteDataPage* page, const void* buf, u32 bufsize)
{
	u8* ptr= (u8*)buf;
	nnsliteMemCell* lastCell = page->lastCell;
	nnsliteMemCell* newCell = NULL;
	u32 leftsize = lastCell->capacity - lastCell->usedsize; /* Get the free buffer size of the last cell*/
	while (leftsize < bufsize) /* if the free size less than the buffer's size */
	{
		memcpy(lastCell->buf+lastCell->usedsize, ptr, leftsize);
		/* Move the pointer of the buffer */
		ptr += leftsize;
		bufsize -= leftsize;
		lastCell->usedsize += leftsize;
		/* Create a new cell */
		newCell = (nnsliteMemCell*)malloc(sizeof(nnsliteMemCell));
		if (newCell == NULL)
		{
			assert(FALSE);
			return NNSLITE_RET_ERR_ALLOCATE_MEM;
		}
		/* Initialize the new cell and link it to the last cell */
		newCell->usedsize = 0;
		newCell->capacity = NNSLITE_CELL_CAPACITY;
		newCell->nextCell = NULL;
		lastCell->nextCell = newCell;
		lastCell = newCell;
		/* Update the page information */
		page->lastCell = lastCell;
		page->totalusedsize += leftsize;
		page->totalcapacity += NNSLITE_CELL_CAPACITY;
		/* Update the new left size of the last cell*/
		leftsize = NNSLITE_CELL_CAPACITY;
	}
	/* Copy the buffer to the last cell */
	memcpy(lastCell->buf+lastCell->usedsize, ptr, bufsize);
	lastCell->usedsize += bufsize;
	page->totalusedsize += bufsize;

	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteDataPageWriteObj(nnsliteDataPage* page, const nnsliteObject* object)
{
	s32 ret;
	ret = nnsliteDataPageWrite(page,&object->id, sizeof(u64));
	NNSLITE_ON_FAILED_RETURN(ret);
	ret = nnsliteDataPageWrite(page,&object->size, sizeof(u32));
	NNSLITE_ON_FAILED_RETURN(ret);	
	ret = nnsliteDataPageWrite(page,object->data, object->size);
	NNSLITE_ON_FAILED_RETURN(ret);
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteDataPageCreateCursor(nnsliteDataPage* page, nnsliteDataPageCursor* cur)
{
	cur->curCell = page->firstCell;
	cur->offsetInCurCell = 0;
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteDataPageRead(nnsliteDataPageCursor* cur, void* buf, u32 bufsize)
{
	u8* ptr = (u8*)buf;
	u32 leftsize = cur->curCell->usedsize - cur->offsetInCurCell;
	while (leftsize < bufsize) 
	{
		memcpy(ptr, cur->curCell->buf+cur->offsetInCurCell, leftsize);
		bufsize -= leftsize;
		ptr += leftsize;
		
		cur->curCell = cur->curCell->nextCell;
		if (cur->curCell == NULL)
		{
			assert(FALSE);
			return NNSLITE_RET_ERR_DATAPAGE;
		}
		cur->offsetInCurCell = 0;
		leftsize = cur->curCell->usedsize - cur->offsetInCurCell;
	}
	memcpy(ptr, cur->curCell->buf+cur->offsetInCurCell, bufsize);
	cur->offsetInCurCell += bufsize;
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteDataPageCursorIsEnd(nnsliteDataPageCursor* cur)
{
	if (cur->curCell->nextCell == NULL)
	{
		if (cur->offsetInCurCell == cur->curCell->usedsize)
			return NNSLITE_RET_TRUE;
	}
	return NNSLITE_RET_FALSE;
}

NNSLITE_RET nnsliteDataPageNextObject(nnsliteDataPageCursor* cur, nnsliteObject* object)
{
	s32 ret;
	u32 size;
	u64 id;

	ret = nnsliteDataPageRead(cur, &id, sizeof(u64));
	NNSLITE_ON_FAILED_RETURN(ret);
	object->id = id;
	
	ret = nnsliteDataPageRead(cur, &size, sizeof(u32));
	NNSLITE_ON_FAILED_RETURN(ret);

	if (size > object->size && object->data != NULL)
	{
		free(object->data);
		object->data = (void*)malloc(size);
		object->size = size;
	}

	if (object->data == NULL)
	{
		object->data = (void*)malloc(size);
		object->size = size;
	}

	ret = nnsliteDataPageRead(cur, object->data, size);
	NNSLITE_ON_FAILED_RETURN(ret);

	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteDataPageWriteTo(nnsliteDataPage* page, FILE* fp)
{
	nnsliteMemCell *cell = page->firstCell;
	fwrite(&page->totalusedsize, sizeof(u64),1,fp);
	while(cell != NULL)
	{
		fwrite(cell->buf,cell->usedsize, 1, fp);
		cell = cell->nextCell;
	}
	return NNSLITE_RET_OK;
}

NNSLITE_RET nnsliteDataPageReadFrom(nnsliteDataPage* page, FILE* fp)
{
	u64 totalusedsize;
	u64 readsize;
	u8* tempbuf = (u8*)malloc(NNSLITE_CELL_CAPACITY);
	
	nnsliteDataPageFree(page);
	nnsliteDataPageInit(page);

	fread(&totalusedsize, sizeof(u64),1,fp);
	readsize = 0;
	while(readsize < totalusedsize)
	{
		u32 bufsize;
		if (totalusedsize - readsize > NNSLITE_CELL_CAPACITY)
			bufsize = NNSLITE_CELL_CAPACITY;
		else
			bufsize = (u32)(totalusedsize - readsize);
		fread(tempbuf,bufsize,1,fp);
		nnsliteDataPageWrite(page, tempbuf, bufsize);
		readsize += bufsize;
	}

	free(tempbuf);
	return NNSLITE_RET_OK;
}

